// 常用正则表达式合集:
// 验证整数: ^[0-9]*$
// 验证n位的数字: ^\d{n}$
// 验证至少n位数字: ^\d{n,}$
// 验证m-n位的数字: ^\d{m,n}$
// 验证零和非零开头的数字: ^(0|[1-9][0-9]*)$
// 验证有两位小数的正实数: ^[0-9]+(.[0-9]{2})?$
// 验证有1-3位小数的正实数: ^[0-9]+(. [0-9]{1,3})?$
// 验证非零的正整数: ^\+?[1-9][0-9]*$ 
// 验证非零的负整数: ^\-[1-9][0-9]*$
// 验证非负整数(正整数+ 0)   ^\d+$
// 验证非正整数(负整数+ 0)   ^((-\d+)|(0+))$
// 验证长度为3的字符: ^.{3}$
// 验证由26个英文字母组成的字符串: ^[A-Za-z]+$ 
// 验证由26个大写英文字母组成的字符串: ^[A-Z]+$
// 验证由26个小写英文字母组成的字符串: ^[a-z]+$
// 验证由数字和26个英文字母组成的字符串: ^[A-Za-Z0-9]+$
// 验证由数字、26个英文字母或者下划线组成的字符串: ^\W+$ 
// 验证用户密码:^[a-ZA-z]\w{5,17}$ 正确格式为:以字母开头，长度在6-18之间，只能包含字符、数字和下划线。
// 验证是否含有^%&" ,;=?$\"等字符: [^%&' ,;=?$\x22]+
// 验证汉字: ^[\u4e00- \u9fa5],{0,}$
// 验证Email地址: ^\w+[-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\W+)*$ 
// 验证InternetURl: ^http://([\w-]+\.)+[\w-]+(/[\W-./?%&=]*)?$ ; ^[a-ZA-z]+://(w+(-w+)*)(.(w+(-w+)*))*(?S*)?$ 
// 验证电话号码: ^(\d{3,4}|\d{3,4}- )?\d{7,8}$: -- 正确格式为: XXXX-XXXXXXX, XXXX XXXxXXX, XXX- XXXXXX, XXX- XXXXXXX, XXXXXX, XXXXXXX。
// 验证身份证号(15位或18位数字) : ^\d{15}|\d{}18$
// 验证年的12个月: ^(0?[1-9]|1[0-2])$ 正确格式为:“01”“-”和“1”12”
// 验证一个月的31天: ^((0?[1-9])1((1|2)[0-9])|30|31)$   正确格式为: 01、 09和1、 31。
// 整数: ^-?\d+$
// 非负浮点数(正浮点数+ 0) : ^\d+(\. \d+)?$
// 正浮点数^(([0-9]+\. [0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$ 
// 非正浮点数(负浮点数+ 0) ^((-\d+(\.\d+)?)|(0+(\.0+)?))$
// 负浮点数^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$
// 浮点数^(-?\d+)(\.\d+)?$

// 在元素前面插入元素
function before(dom1, dom2) {
    dom2.parentNode.insertBefore(dom1, dom2)
}

// 在元素后面插入元素
function after(dom1, dom2) {
    dom2.parentNode.insertBefore(dom1, dom2.nextSibling)
}

// 浏览器兼容
function getNodes(dom) {
    // 返回包含是所有节点的数组
    var arr = [];
    // 定义正则
    var reg = /^\s+$/;
    // 遍历所有节点，过滤换行符文本节点
    for (var i = 0; i < dom.childNodes.length; i++) {
        // 如果是文本节点要过滤掉换行符
        if (dom.childNodes[i].nodeType === 3) {
            // 判断文本的内容，date或者nodeValue
            console.log(reg.test(dom.childNodes[i].data), dom.childNodes[i].data);
            if (!reg.test(dom.childNodes[i].data)) {
                // 不是换行符
                arr.push(dom.childNodes[i])
            }
        } else {
            // 其他类型节点，直接存储
            arr.push(dom.childNodes[i])
        }

    }
    return arr;
}

// 在元素的后面插入元素
function insertAfter(parent, child, next) {
    return parent.insertBefore(child, next.nextSibling);
}

// 在子元素前面插入元素
function prependChild(parent, child) {
    return parent.insertBefore(child, parent.firstChild)
}


// 封装getStyle方法，可以在不同浏览器下获取样式
// @obj      元素对象
// @key      属性名称
// return    获取的样式
function getStyle(obj, key) {
    // 浏览器是否支持getComputed方法，支持就使用
    if (window.getComputedStyle) {
        //通过该方法获取
        //已经确定了该方法存在，就可以直接使用了
        // 方法一
        return getComputedStyle(obj)[key];
        // 方法二
        // 将key转成横杠法
        // key = key.replace(/([A-Z])/g,function(match,$1) {
        //     return '-' + $1.toLowerCase();
        // })
        // return getComputedStyle(obj).getPropertyValue(key);
    } else {
        var style = ocj.currentStyle;
        // 如果有样式，可以获取
        if (style) {
            // 将横杠法转成驼峰式命名
            key = key.replace(/-([a-z]?)/g, function (match, $1) {
                return ($1 || '').toUpperCase();
            })
            return style[key]
        } else {
            // 没有样式，要提示
            alert('你的浏览器不支持获取计算样式功能')
        }
    }
}

// 实现animate
// @obj      操作的元素
// @obj      样式对象
// @time     移动时间
// @callback 动画执行完毕，执行的回调函数
// 实现animate
function animate(dom, obj, time, callback) {
    // 在规定时间内，移动到某一个位置，假设30毫秒移动一次
    // 每次移动的距离，步长 = 移动的总距离 / 移动的总次数
    // 移动的总次数 = 总时长 / 移动的间隔
    // 定义移动次数
    var count = 0;
    // 计算总次数,不去整不影响结果
    var total = parseInt((time || 1000) / 30);
    // 总距离的获取的必要条件是：最终的样式与当前样式
    // 当前样式：通过元素获取
    // 最终样式：通过obj参数获取
    // 获取当前样式
    var style = {};
    for (var key in obj) {
        // 存储当前样式
        // 为了计算方便，存储数字，而不是字符串
        style[key] = parseInt(getStyle(dom, key))
    }
    var step = {};
    // 获取每一个样式的步长
    for (var key in obj) {
        // 存储步长
        step[key] = (parseInt(obj[key]) - style[key]) / total;
    }
    // 执行动画就是执行setInterval,当达到总次数时候，终止动画执行
    // 定义动画句柄
    var timebar;
    // 启动定时器
    timebar = setInterval(function () {
        // 更新次数
        count++;
        // 修改样式
        for (var key in step) {
            dom.style[key] = style[key] + step[key] * count + 'px';
        }
        // 判断终止条件
        if (count >= total) {
            //修正样式
            for (var key in obj) {
                // 判断属性值是否是字符串，是字符串直接赋值，不是加上单位
                dom.style[key] = typeof obj[key] === 'string' ? obj[key] : obj[key] + 'px';
            }
            // 清空定时器
            clearInterval(timebar);
            // 传递了callback要执行callback
            callback && callback();
        }
    }, 30)

}

// 实现bindEvent
// @dom    元素
// @type   事件类型
// @fn     事件回调函数
function bindEvent(dom, type, fn) {
    // 能力检测：判断方法是否存在，存在使用，不存在不使用
    if (dom.addEventListener) {
        // dom2级绑定方式，都在冒泡阶段触发
        dom.addEventListener(type, fn);
    } else if (dom.attachEvent) {
        // 针对ie绑定事件
        dom.attachEvent('on' + type, function (e) {
            // 兼容性
            e.target = e.srcElement;
            e.currentTarget = this;
            // 让fn在dom上执行
            fn.call(dom, e)
        });
    } else {
        // 缓存
        var oldFn = dom['on' + type];
        // dom0级绑定方式
        dom['on' + type] = function (e) {
            // 如果已经绑定过，先执行之前的
            oldFn && oldFn(e || window.event);
            // 在执行新的
            fn(e || window.event);
        }
    }
}


// 移除事件
function removeEvent(dom, type, fn) {
    if(dom.removeEventListener) {
        dom.removeEventListener(type, fn)
    } else if (dom.detachEvent) {
        dom.detachEvent('on' + type, fn)
    } else {
        dom['on' + type] = null
    }
}